package com.danicsoft.daide.command;

import java.util.ArrayList;
import java.util.List;

import com.danicsoft.daide.token.DiplomaticMessageListener;
import com.danicsoft.daide.token.StandardToken;
import com.danicsoft.daide.token.Token;
import com.danicsoft.daide.token.TokenGroup;
import com.danicsoft.daide.token.TokenMap;
import com.danicsoft.daide.token.TokenMap.Commands;

/**
 * This class is responsible for converting groups of tokens into commands and
 * notifying and interested objects when new commands are received.
 * 
 * @author Daniel Yule (daniel.yule@gmail.com)
 * @version 1.0
 */
public class CommandParser implements DiplomaticMessageListener {

	/**
	 * The handlers to notify when a new command is recieved.
	 */
	private List<CommandHandler> handlers = new ArrayList<CommandHandler>();

	public void diplomaticMessageReceived(TokenGroup tokenGroup) {
		Command c;
		try {
			c = parse(tokenGroup);

			if (c != null) {
				switch (c.getStatus()) {
					case ACCEPTED:
						acceptedCommand(((ReducibleCommand) c).command);
						break;
					case REJECTED:
						rejectedCommand(((ReducibleCommand) c).command);
						break;
					case TRANSMITTING:
						recievedCommand(c);
						break;

				}
			}

		}
		catch (ParseException e) {
			for (CommandHandler handler : handlers) {
				handler.CommandError(tokenGroup, e.getMessage());
			}
		}

	}

	/**
	 * Add a handler to this parser. Each handler is called every time this
	 * parser recieves a new command.
	 * 
	 * @param handler
	 *        The handler to add.
	 */
	public void addHandler(CommandHandler handler) {
		handlers.add(handler);
	}

	/**
	 * Convert the given <code>TokenGroup</code> to the command it represents
	 * 
	 * @param tokens
	 *        The tokens to generate the command from
	 * @return A <code>Command</code> which corresponds to the given tokens.
	 * @throws ParseException
	 *         If the command was malformed.
	 */
	public static Command parse(TokenGroup tokens) throws ParseException {
		Token firstToken = tokens.next();
		// XXX Should parse simply look at a HashMap of token -> classes?

		if (firstToken.getType() == TokenMap.COMMANDS) {
			StandardToken sToken = (StandardToken) firstToken;
			switch (sToken.getData()) {
				case Commands.YES:
					return new YesCommand(tokens);
				case Commands.REJ:
					return new RejectedCommand(tokens);
				case Commands.NME:
					return new NameCommand(tokens);
				case Commands.IAM:
					return new IamCommand(tokens);
				case Commands.OBS:
					return new ObserverCommand(tokens);
				case Commands.MAP:
					return new MapCommand(tokens);
				case Commands.MDF:
					return new MapDefinitionCommand(tokens);
				case Commands.HLO:
					return new HelloCommand(tokens);
				case Commands.SCO:
					return new SupplyOwnershipCommand(tokens);
				case Commands.NOW:
					return new NowCommand(tokens);
				case Commands.ORD:
					return new OrderCommand(tokens);
				case Commands.OFF:
					return new PissOffCommand(tokens);
				case Commands.PRN:
					return new ParenthesisCommand(tokens);
				case Commands.SUB:
					return new SubmitCommand(tokens);
				case Commands.THX:
					return new ThanksCommand(tokens);
				case Commands.MIS:
					return new MissingCommand(tokens);
				case Commands.ADM:
					return new AdminMessageCommand(tokens);
				case Commands.GOF:
					return new GoFlagCommand(tokens);
				case Commands.HST:
					return new HistoryCommand(tokens);
				case Commands.NOT:
					return new NotCommand(tokens);
				case Commands.SVE:
					return new SaveCommand(tokens);
				case Commands.TME:
					return new TimeCommand(tokens);
				case Commands.HUH:
					return new HuhCommand(tokens);
				case Commands.DRW:
					return new DrawCommand(tokens);
				case Commands.SLO:
					return new SoloCommand(tokens);
				case Commands.SMR:
					return new SummaryCommand(tokens);


			}
		}

		return null;

	}

	/**
	 * Remove a <code>CommandHandler</code> from the list of handlers that are
	 * notified when a new command is sent.
	 * 
	 * @param handler
	 *        The handler to remove
	 */
	public void removeHandler(CommandHandler handler) {
		handlers.remove(handler);
	}

	/**
	 * Called when a command is sent with a YES token.
	 * 
	 * @param c
	 *        The <code>Command</code> that has been accepted.
	 */
	private void acceptedCommand(Command c) {
		for (CommandHandler handler : handlers) {
			handler.CommandAccepted(c);
		}

	}

	/**
	 * Called when a command is sent.
	 * 
	 * @param c
	 *        The <code>Command</code> that has been sent.
	 */
	private void recievedCommand(Command c) {
		for (CommandHandler handler : handlers) {
			handler.CommandReceived(c);
		}

	}

	/**
	 * Called when a command is sent with a REJ token.
	 * 
	 * @param c
	 *        The <code>Command</code> that has been rejected.
	 */
	private void rejectedCommand(Command c) {
		for (CommandHandler handler : handlers) {
			handler.CommandRejected(c);
		}

	}

}
